import type { PageType } from './page_handler/type'
import { ConfigManager } from '../module/config_manager'
import {
  APP_CONFIG,
  BodyAttrs,
  BodyClasses,
  EVENT_EMITTER_NAMES,
  HOSTNAME,
  logger,
  MODULE_CONFIG,
  SignType,
  SUPPORTED_PLATFORMS,
} from '../types/constants'
import { SYMBOLS } from '../types/interfaces'
import { FeatureToggle } from '../user_center/feature_toggle'
import { BackgroundImageService } from '../user_center/tab/personalization/background_image/service'
import { GreasyFork } from '../user_center/tab/personalization/custom_css/theme/others/greasy_fork/page/lib'
import { PageThemeService } from '../user_center/tab/personalization/page_theme/service'
import { PageManager } from './abstract/page_manager'
import { DocumentManager } from './document_manager'
import { EventEmitter } from './event_emitter'
import { PageHandler } from './page_handler'
import { BaiduPageType } from './page_handler/platform/baidu/types'
import { SogouPageType } from './page_handler/platform/sogou/types'
import { UNDEFINED_PAGE_TYPE } from './page_handler/type'
import { PollingHandler } from './polling_handler'
import { TimerManager } from './timer_manager'
import { ToolKit } from './utils/toolkit'

/**
 * Body 处理器
 */
export class BodyHandler {
  // @Decorators.Identity
  readonly id: string = 'body-handler'
  readonly name: string = 'BodyHandler'
  // @Decorators.PublicState
  private static instance: BodyHandler
  // @Decorators.Dependency
  private readonly eventEmitter: EventEmitter = EventEmitter.getInstance()
  private readonly configManager: ConfigManager = ConfigManager.getInstance()
  private readonly pollingHandler: PollingHandler = PollingHandler.getInstance()
  // @Decorators.LazyDependency
  private pageThemeService: PageThemeService | null = null
  private backgroundImageService: BackgroundImageService | null = null
  private pageManager: PageManager | null = null
  private timerManager: TimerManager | null = null
  // @Decorators.Core
  private animationFrameId: number | null = null // 帧回调管理器，避免造成不必要计算和渲染
  private elementObserver: ResizeObserver | null = null
  // @Decorators.UIElement
  private readonly defaultFontSize: string = '16px'
  // @Decorators.TemporaryState
  private resizeWindowListenerAttached = false
  private resizeElementListenerAttached = false
  private lastPageType: PageType | null = null
  private currentPageType: PageType | null = null

  /**
   * 获取单例实例
   */
  public static getInstance(): BodyHandler {
    if (!BodyHandler.instance) {
      BodyHandler.instance = new BodyHandler()
    }
    return BodyHandler.instance
  }

  /**
   * 初始化 body 设置
   */
  public init(): void {
    ToolKit.async.waitForBodyElement().then(() => {
      DocumentManager.getInstance().applyLoadingStyles(true, { target: 'body' })

      this.setupBodyIdentifier()
      this.setLoginStatus()

      // 必须先更新页面类型
      this.updatePageType()

      this.updateRootFontSize()
      this.updateScrollbarOffset()
      this.updateResponsiveClasses()

      // this.setupWindowResizeListener()
      // ResizeObserver监听 document.body，不需要额外添加 window.resize监听
      this.setupElementResizeListener()
      this.setupSelectionListener()
      this.setupUrlChangeObserver()
    })

    ToolKit.async.onWindowLoad(() => {
      this.retryGetPageType()
    })
  }

  /**
   * 清理资源
   */
  public destroy(): void {
    this.stopWindowResizeListener()
    this.stopElementResizeListener()
    this.stopSelectionListener()
  }

  /**
   * 设置 body ID
   */
  private setupBodyIdentifier(): void {
    const body = document.body

    if (!body) {
      logger.error(`[${this.name}] body 元素不存在`)
      return
    }

    const bodyId = body.getAttribute('id')

    if (bodyId !== MODULE_CONFIG.PAGE.body.id) {
      body.id = MODULE_CONFIG.PAGE.body.id
    }
  }

  /**
   * 设置用户登录状态
   */
  private setLoginStatus(): void {
    let isLoggedIn: boolean | undefined

    if (HOSTNAME === SUPPORTED_PLATFORMS.ZHI_HU_QUESTION) {
      const selector = '.AppHeader .PageHeader .QuestionHeader-content'
      const handler = () => {
        isLoggedIn = $(selector)?.hasClass('isLogin') ?? false

        if (isLoggedIn === undefined)
          return

        $(document.body).attr(BodyAttrs.IS_LOGGED_IN, String(isLoggedIn))
      }

      this.pollingHandler.api.awaitElement(selector, handler)
    }
    else if (HOSTNAME === SUPPORTED_PLATFORMS.GREASY_FORK) {
      isLoggedIn = GreasyFork.getInstance().getSignType() === SignType.SignOut

      $(document.body).attr(BodyAttrs.IS_LOGGED_IN, String(isLoggedIn))
    }
    else if (HOSTNAME === SUPPORTED_PLATFORMS.BIAN) {
      isLoggedIn = $('.head .user .user-name').length > 0

      $(document.body).attr(BodyAttrs.IS_LOGGED_IN, String(isLoggedIn))
    }
  }

  /**
   * 更新根字体大小
   */
  private updateRootFontSize(): void {
    let fontSize: string
    if (HOSTNAME === SUPPORTED_PLATFORMS.BAI_DU) {
      switch (this.currentPageType) {
        case BaiduPageType.NotFound:
          fontSize = this.defaultFontSize
          break
        default:
          fontSize = '100px'
      }
    }
    else if (HOSTNAME === SUPPORTED_PLATFORMS.SO_GOU) {
      switch (this.currentPageType) {
        case SogouPageType.Video:
          fontSize = '14px'
          break
        case SogouPageType.NotFound:
          fontSize = '14px'
          break
        default:
          fontSize = '100px'
      }
    }
    else {
      fontSize = this.defaultFontSize
    }

    document.documentElement.style.setProperty(
      MODULE_CONFIG.PAGE.body.styleVar.rootFontSize,
      fontSize,
    )
  }

  /**
   * 更新滚动条偏移量
   */
  private updateScrollbarOffset(): void {
    document.documentElement.style.setProperty(
      MODULE_CONFIG.PAGE.body.styleVar.scrollbarOffset,
      `${window.innerWidth - document.documentElement.clientWidth}px`,
    )
  }

  /**
   * 更新响应式类
   */
  private updateResponsiveClasses(): void {
    const isMobile = window.innerWidth <= 768
    document.body.classList.toggle(BodyClasses.IS_MOBILE, isMobile)
    document.body.classList.toggle(BodyClasses.IS_DESKTOP, !isMobile)
  }

  /**
   * 更新页面类型
   */
  updatePageType(pageType?: PageType): void {
    this.currentPageType = pageType || PageHandler.detectCurrentPageType()

    document.body.setAttribute(BodyAttrs.PAGE_TYPE, this.currentPageType)

    this.handlePageTypeChange()
  }

  /**
   * 重试获取页面类型
   */
  retryGetPageType(): void {
    if (this.currentPageType !== UNDEFINED_PAGE_TYPE)
      return

    if (!this.timerManager)
      this.timerManager = TimerManager.getInstance()

    this.timerManager.createTimerTask({
      id: 'refresh-page-type',
      delay: 2000,
      callback: () => {
        this.updatePageType()
      },
    })
  }

  /**
   * 更新配置管理器
   */
  updateConfigManager(): void {
    this.configManager.handlePageTypeChange(this.currentPageType!)
  }

  /**
   * 添加窗口大小变化监听器
   */
  setupWindowResizeListener(): void {
    if (!this.resizeWindowListenerAttached) {
      window.addEventListener('resize', this.handleWindowResize)
      this.resizeWindowListenerAttached = true
    }
  }

  /**
   * 移除窗口大小变化监听器
   */
  private stopWindowResizeListener(): void {
    if (this.resizeWindowListenerAttached) {
      window.removeEventListener('resize', this.handleWindowResize)
      this.resizeWindowListenerAttached = false
    }
  }

  /**
   * 添加元素大小变化监听器
   */
  private setupElementResizeListener(): void {
    if (typeof ResizeObserver !== 'undefined') {
      this.elementObserver = new ResizeObserver(this.handleElementResize)
      this.elementObserver.observe(document.body)
      this.resizeElementListenerAttached = true
    }
  }

  /**
   * 移除元素大小变化监听器
   */
  private stopElementResizeListener(): void {
    if (this.resizeElementListenerAttached) {
      // 清理 ResizeObserver
      this.elementObserver?.disconnect()
      this.elementObserver = null

      // 清理 animationFrame
      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId)
        this.animationFrameId = null
      }

      this.resizeElementListenerAttached = false
    }
  }

  /**
   * 添加选择变化监听器
   */
  private setupSelectionListener(): void {
    if (!APP_CONFIG.selectionListener)
      return

    const supportedPlatforms = [
      SUPPORTED_PLATFORMS.BAI_DU,
    ]

    if (!supportedPlatforms.includes(HOSTNAME))
      return

    document.addEventListener('selectionchange', this.handleSelectionChange)
  }

  /**
   * 处理选择变化事件
   */
  private handleSelectionChange = () => {
    const selection = ToolKit.browser.selection.getCurrentSelection()
    const hasSelection = selection && selection.toString().trim().length > 0

    // $(document.body).attr(BodyAttrs.HAS_SELECTION, String(hasSelection))

    if (!hasSelection)
      return

    this.eventEmitter.emit(EVENT_EMITTER_NAMES.selectionChanged)
  }

  /**
   * 移除选择变化监听器
   */
  private stopSelectionListener(): void {
    document.removeEventListener('selectionchange', this.handleSelectionChange)
  }

  /**
   * 设置 URL 变化观察器
   */
  private setupUrlChangeObserver(): void {
    this.eventEmitter.on(EVENT_EMITTER_NAMES.urlChanged, () => this.updatePageClassification())
  }

  /**
   * 更新页面分类
   */
  private updatePageClassification(): void {
    this.currentPageType = PageHandler.detectCurrentPageType()

    this.updateRootFontSize()
    this.updatePageType(this.currentPageType)

    logger.debug(`[${this.name}] 页面类型更新: ${this.lastPageType} ${SYMBOLS.ARROWS.RIGHT} ${this.currentPageType}`)
    this.lastPageType = this.currentPageType
  }

  /**
   * 处理页面类型变化
   */
  handlePageTypeChange(): void {
    if (!this.checkPageTypeStatus()) {
      if (!this.lastPageType) {
        this.lastPageType = this.currentPageType
      }

      return
    }

    this.updateConfigManager()
    FeatureToggle.getInstance().refreshFeature()
    this.handleAppChange()

    this.lastPageType = this.currentPageType
  }

  handleAppChange(): void {
    this.setDependencies()

    // 页面类型->未定义页面类型
    if (this.lastPageType !== UNDEFINED_PAGE_TYPE && this.currentPageType === UNDEFINED_PAGE_TYPE) {
      const supportPlatforms = [
        SUPPORTED_PLATFORMS.ZHI_HU_QUESTION,
      ]

      // 移除主题样式、背景图片
      if (supportPlatforms.includes(HOSTNAME)) {
        ToolKit.browser.platform.markScriptSupportStatus(false)
        this.pageThemeService?.unregisterStyles()
        this.backgroundImageService?.destroy()
      }
    }
    else if (this.lastPageType === UNDEFINED_PAGE_TYPE && this.currentPageType !== UNDEFINED_PAGE_TYPE) {
      const supportPlatforms = [
        SUPPORTED_PLATFORMS.ZHI_HU_QUESTION,
      ]

      // 添加主题样式、背景图片
      if (supportPlatforms.includes(HOSTNAME)) {
        ToolKit.browser.platform.markScriptSupportStatus(true)
        this.pageThemeService?.registerStyles(ToolKit.browser.theme.getCurrentTheme())
        this.backgroundImageService?.apply(this.pageManager?.getBackgroundImageUrl() || '')
      }
    }
  }

  setDependencies(): void {
    if (!this.pageThemeService)
      this.pageThemeService = PageThemeService.getInstance()

    if (!this.backgroundImageService)
      this.backgroundImageService = new BackgroundImageService()

    if (!this.pageManager)
      this.pageManager = PageManager.getInstance()
  }

  /**
   * 检查页面类型是否发生变化
   */
  checkPageTypeStatus(): boolean {
    return Boolean(this.lastPageType) && this.lastPageType !== this.currentPageType
  }

  /**
   * 处理窗口大小变化
   */
  private handleWindowResize = ToolKit.func.throttle(() => {
    this.updateScrollbarOffset()
    this.updateResponsiveClasses()
  }, 200)

  /**
   * 处理元素大小变化（ResizeObserver 本身已经经过浏览器优化，通常不需要额外节流；节流可能导致错过重要的尺寸变化事件）
   */
  private handleElementResize = (_entries: ResizeObserverEntry[]) => {
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
      this.animationFrameId = null
    }

    this.animationFrameId = requestAnimationFrame(() => {
      this.updateScrollbarOffset()
      this.updateResponsiveClasses()
      this.updateQuickScroll()
    })
  }

  /**
   * 更新快捷滚动状态
   */
  updateQuickScroll(): void {
    this.eventEmitter.emit(EVENT_EMITTER_NAMES.refreshQuickScroll)
  }

  /**
   * 检测页面是否可滚动
   */
  static checkScrollable(): boolean {
    return document.body.scrollHeight > window.innerHeight
  }
}
